# @Version        : 1.0
# @Update Time    : 2025/9/15 22:58
# @File           : fa_strategy.py
# @IDE            : PyCharm
# @Desc           : 策略模式实现，参考Sa-Token的SaStrategy类
import inspect
import logging
import re
import secrets
import threading
import uuid
from typing import Any, Callable, Dict, List, Type, TypeVar, Awaitable
from authx.core.errors import AuthError, SessionError, ServiceError
from authx.models.session import AuthSessionModel
from authx.utils.fox_util import FoxUtil
from authx.utils.singleton import Singleton

# 配置日志记录器
logger = logging.getLogger(__name__)

# 定义类型变量
T = TypeVar("T")


class AuthStrategy(metaclass=Singleton):
    """
    Sa-Token 策略类，用于自定义各种处理逻辑

    企业级特性:
    - 线程安全的单例模式
    - 完整的类型注解
    - 增强的异常处理
    - 详细的日志记录
    - 配置验证机制
    - 健康检查功能
    """

    # 类锁，确保线程安全
    _lock = threading.RLock()

    def __init__(self):
        """
        初始化策略类，设置默认的各种处理逻辑
        使用双检锁模式确保线程安全的懒加载初始化
        """
        # 检查是否已经初始化过，避免重复初始化
        if hasattr(self, "initialized"):
            return

        # 使用线程锁确保初始化过程的线程安全
        with self._lock:
            # 双重检查锁定模式
            if hasattr(self, "initialized"):
                return

            logger.debug("初始化FaStrategy实例")

            # 初始化解码器集合
            self._validators: Dict[str, Callable[[], bool]] = {}

            # 创建 Token 的策略
            def create_token(login_id: Any, login_type: str) -> str:
                """生成token的默认策略"""
                try:
                    # 根据配置的tokenStyle生成不同风格的token
                    # 避免循环依赖，直接从全局配置获取token_style
                    from authx.settings.config import settings

                    # 直接使用全局配置，避免循环调用AuthManager.get_logic
                    token_style = settings.TOKEN_STYLE.value
                    match str(token_style):
                        case "uuid":
                            return str(uuid.uuid4())
                        case "simple-uuid":
                            return str(uuid.uuid4()).replace("-", "")
                        case "random-32":
                            return secrets.token_hex(16)
                        case "random-64":
                            return secrets.token_hex(32)
                        case "random-128":
                            return secrets.token_hex(64)
                        case "tik":
                            return (
                                secrets.token_hex(1)
                                + "_"
                                + secrets.token_hex(7)
                                + "_"
                                + secrets.token_hex(8)
                                + "__"
                            )
                        case _:
                            # 默认使用uuid风格
                            logger.warning(
                                f"未知的token_style: {token_style}，使用uuid作为默认值"
                            )
                            return str(uuid.uuid4())
                except Exception as e:
                    logger.error(f"生成token失败: {str(e)}", exc_info=True)
                    # 即使配置出错也要保证能生成token
                    return str(uuid.uuid4())

            self.create_token: Callable[[Any, str], str] = create_token

            # 添加验证器
            self._validators["create_token"] = lambda: callable(self.create_token)

            # 创建 Session 的策略
            def create_session(session_id: str) -> AuthSessionModel:
                """创建session的默认策略"""
                try:
                    return AuthSessionModel(session_id)
                except Exception as e:
                    logger.error(f"创建session失败: {str(e)}", exc_info=True)
                    raise SessionError(message=f"创建会话失败: {str(e)}") from e

            self.create_session: Callable[[str], AuthSessionModel] = create_session
            self._validators["create_session"] = lambda: callable(self.create_session)

            # 反序列化 SaSession 时默认指定的类型
            self.session_class_type: Type[AuthSessionModel] = AuthSessionModel

            # 判断：集合中是否包含指定元素（模糊匹配）
            def has_element(lst: List[str], element: str) -> bool:
                """
                检查集合中是否包含指定元素，支持精确匹配和模糊匹配

                :param lst: 字符串列表
                :param element: 要查找的元素
                :return: 是否包含该元素
                """
                # 空集合直接返回false
                if lst is None or len(lst) == 0:
                    return False

                # 先尝试一下简单匹配，如果可以匹配成功则无需继续模糊匹配
                if element in lst:
                    return True

                # 开始模糊匹配
                try:
                    for patt in lst:
                        if FoxUtil.vague_match(patt, element):
                            return True
                except Exception as e:
                    logger.error(f"模糊匹配过程中出错: {str(e)}", exc_info=True)
                    # 出错时回退到精确匹配
                    return element in lst

                # 走出for循环说明没有一个元素可以匹配成功
                return False

            self.has_element: Callable[[List[str], str], bool] = has_element
            self._validators["has_element"] = lambda: callable(self.has_element)

            # 生成唯一式 token 的算法
            import time

            # 在 FaStrategy.__init__ 里，替换/新增这两个方法

            async def generate_unique_token(
                element_name: str,
                max_try_times: int,
                create_token_function: Callable[[], str],
                check_token_function: Callable[[str], Awaitable[bool]],
            ) -> str:
                """
                同步版本：仅支持同步函数。
                如传入异步函数，会抛出异常提示使用 generate_unique_token_async。
                """
                # 如果传入的是异步函数，明确报错，避免在事件循环中误用
                attempt_count = 0
                unlimited = max_try_times < 0
                soft_cap = 10_000 if unlimited else max_try_times

                while unlimited or attempt_count < soft_cap:
                    attempt_count += 1
                    try:
                        token = create_token_function()
                        if not token or not isinstance(token, str):
                            logger.warning("生成的token无效: %r", token)
                            continue

                        if unlimited:
                            logger.debug("跳过唯一性验证，直接返回token")
                            return token

                        try:
                            if inspect.iscoroutinefunction(check_token_function):
                                status = await check_token_function(token)
                            else:
                                status = check_token_function(token)
                            if not status:
                                logger.debug(
                                    "成功生成唯一%s，尝试次数: %d",
                                    element_name,
                                    attempt_count,
                                )
                                return token
                        except Exception as e:
                            logger.exception(
                                f"检查token有效性时出错（第%s次）{e}", attempt_count
                            )
                            continue

                    except Exception as e:
                        logger.exception(
                            f"生成token过程中出错（第%s次）{e}", attempt_count
                        )

                    # 可选：加短暂 sleep，避免热循环（同步环境）
                    time.sleep(min(0.05, 0.001 * attempt_count))

                msg = f"{element_name} 生成失败，已尝试{attempt_count}次，可能算法过于简单或资源池已耗尽"
                logger.error(msg)
                raise ServiceError(msg)

            self.generate_unique_token: Callable[
                [str, int, Callable[[], str], Callable[[str], Awaitable[bool]]],
                Awaitable[str],
            ] = generate_unique_token
            self._validators["generate_unique_token"] = lambda: callable(
                self.generate_unique_token
            )

            # 是否自动续期 active-timeout
            def auto_renew(stp_logic) -> bool:
                """判断是否自动续期的默认策略"""
                try:
                    return stp_logic.get_config_or_global().AUTO_RENEW
                except Exception as e:
                    logger.error(f"检查自动续期配置时出错: {str(e)}", exc_info=True)
                    # 默认不自动续期
                    return False

            self.auto_renew: Callable[[Any], bool] = auto_renew
            self._validators["auto_renew"] = lambda: callable(self.auto_renew)

            # 创建 StpLogic 的算法
            def create_stp_logic(login_type: str) -> Any:
                """创建StpLogic实例的默认策略"""
                try:
                    # 避免循环导入
                    from ..core.auth_logic import AuthLogic

                    # 创建新的实例
                    return AuthLogic(login_type)
                except Exception as e:
                    logger.error(f"创建StpLogic实例失败: {str(e)}", exc_info=True)
                    raise AuthError("auth", f"创建认证逻辑实例失败: {str(e)}") from e

            self.create_stp_logic: Callable[[str], Any] = create_stp_logic
            self._validators["create_stp_logic"] = lambda: callable(
                self.create_stp_logic
            )

            # 路由匹配策略 - 实现基于Ant风格的路由匹配
            def route_matcher(pattern: str, path: str) -> bool:
                """
                路由匹配策略，实现Ant风格的路由匹配

                支持的通配符：
                - ? 匹配单个字符
                - * 匹配0个或多个字符
                - ** 匹配0个或多个目录

                :param pattern: 路由模式
                :param path: 实际路径
                :return: 是否匹配成功
                """
                if pattern is None or path is None:
                    return False

                # 对于精确匹配的情况进行快速处理
                if pattern == path:
                    return True

                # 如果没有通配符，直接返回False
                if "?" not in pattern and "*" not in pattern:
                    return False

                try:
                    # 将Ant风格的通配符转换为正则表达式
                    regex_pattern = pattern
                    # 转义特殊字符
                    regex_pattern = re.escape(regex_pattern)
                    # 替换通配符
                    regex_pattern = (
                        regex_pattern.replace("\\?", ".")
                        .replace("\\*\\*", ".*")
                        .replace("\\*", "[^/]*")
                    )
                    # 添加开始和结束标记
                    regex_pattern = f"^{regex_pattern}$"
                    # 执行匹配
                    return re.match(regex_pattern, path) is not None
                except Exception as e:
                    logger.error(f"路由匹配过程中出错: {str(e)}", exc_info=True)
                    return False

            self.route_matcher: Callable[[str, str], bool] = route_matcher
            self._validators["route_matcher"] = lambda: callable(self.route_matcher)

            # CORS 策略处理函数
            def cors_handle(req, res, sto):
                """CORS处理的默认策略"""
                try:
                    # 默认实现为空，可以被用户自定义实现覆盖
                    pass
                except Exception as e:
                    logger.error(f"CORS处理过程中出错: {str(e)}", exc_info=True)

            self.cors_handle: Callable[[Any, Any, Any], None] = cors_handle
            self._validators["cors_handle"] = lambda: callable(self.cors_handle)

            # 标记已初始化
            self.initialized = True
            logger.debug("FaStrategy实例初始化完成")

    def validate(self) -> bool:
        """
        验证策略配置的有效性

        :return: 所有策略是否有效
        """
        all_valid = True
        invalid_strategies = []

        for name, validator in self._validators.items():
            try:
                if not validator():
                    all_valid = False
                    invalid_strategies.append(name)
            except Exception as e:
                all_valid = False
                invalid_strategies.append(f"{name} (验证过程出错: {str(e)})")
                logger.error(f"验证策略 {name} 时出错: {str(e)}", exc_info=True)

        if invalid_strategies:
            logger.error(f"以下策略无效: {', '.join(invalid_strategies)}")

        return all_valid

    def health_check(self) -> Dict[str, Any]:
        """
        执行健康检查

        :return: 健康检查结果
        """
        result = {
            "status": "healthy",
            "initialized": hasattr(self, "initialized") and self.initialized,
            "valid_strategies": [],
            "invalid_strategies": [],
        }

        # 检查初始化状态
        if not result["initialized"]:
            result["status"] = "unhealthy"
            return result

        # 验证所有策略
        for name, validator in self._validators.items():
            try:
                if validator():
                    result["valid_strategies"].append(name)
                else:
                    result["invalid_strategies"].append(name)
            except Exception:
                result["invalid_strategies"].append(name)

        # 如果有无效策略，设置状态为不健康
        if result["invalid_strategies"]:
            result["status"] = "degraded"

        return result

    # ----------------------- 重写策略 set链式风格 -----------------------

    def set_create_token(
        self, create_token: Callable[[Any, str], str]
    ) -> "AuthStrategy":
        """
        重写创建 Token 的策略

        :param create_token: 创建token的函数
        :return: 当前实例，支持链式调用
        :raises ValueError: 当参数不是可调用对象时抛出
        """
        if not callable(create_token):
            raise ValueError("create_token必须是可调用对象")

        with self._lock:
            self.create_token = create_token
            logger.info("已自定义create_token策略")

        return self

    def set_create_session(
        self, create_session: Callable[[str], AuthSessionModel]
    ) -> "AuthStrategy":
        """
        重写创建 Session 的策略

        :param create_session: 创建session的函数
        :return: 当前实例，支持链式调用
        :raises ValueError: 当参数不是可调用对象时抛出
        """
        if not callable(create_session):
            raise ValueError("create_session必须是可调用对象")

        with self._lock:
            self.create_session = create_session
            logger.info("已自定义create_session策略")

        return self

    def set_has_element(
        self, has_element: Callable[[List[str], str], bool]
    ) -> "AuthStrategy":
        """
        判断：集合中是否包含指定元素（模糊匹配）

        :param has_element: 判断元素是否在集合中的函数
        :return: 当前实例，支持链式调用
        :raises ValueError: 当参数不是可调用对象时抛出
        """
        if not callable(has_element):
            raise ValueError("has_element必须是可调用对象")

        with self._lock:
            self.has_element = has_element
            logger.info("已自定义has_element策略")

        return self

    def set_generate_unique_token(
        self,
        generate_unique_token: Callable[
            [str, int, Callable[[], str], Callable[[str], bool]], str
        ],
    ) -> "AuthStrategy":
        """
        生成唯一式 token 的算法

        :param generate_unique_token: 生成唯一token的函数
        :return: 当前实例，支持链式调用
        :raises ValueError: 当参数不是可调用对象时抛出
        """
        if not callable(generate_unique_token):
            raise ValueError("generate_unique_token必须是可调用对象")

        with self._lock:
            self.generate_unique_token = generate_unique_token
            logger.info("已自定义generate_unique_token策略")

        return self

    def set_auto_renew(self, auto_renew: Callable[[Any], bool]) -> "AuthStrategy":
        """
        是否自动续期

        :param auto_renew: 判断是否自动续期的函数
        :return: 当前实例，支持链式调用
        :raises ValueError: 当参数不是可调用对象时抛出
        """
        if not callable(auto_renew):
            raise ValueError("auto_renew必须是可调用对象")

        with self._lock:
            self.auto_renew = auto_renew
            logger.info("已自定义auto_renew策略")

        return self

    def set_create_stp_logic(
        self, create_stp_logic: Callable[[str], Any]
    ) -> "AuthStrategy":
        """
        创建 StpLogic 的算法

        :param create_stp_logic: 创建StpLogic的函数
        :return: 当前实例，支持链式调用
        :raises ValueError: 当参数不是可调用对象时抛出
        """
        if not callable(create_stp_logic):
            raise ValueError("create_stp_logic必须是可调用对象")

        with self._lock:
            self.create_stp_logic = create_stp_logic
            logger.info("已自定义create_stp_logic策略")

        return self

    def set_route_matcher(
        self, route_matcher: Callable[[str, str], bool]
    ) -> "AuthStrategy":
        """
        路由匹配策略

        :param route_matcher: 路由匹配函数
        :return: 当前实例，支持链式调用
        :raises ValueError: 当参数不是可调用对象时抛出
        """
        if not callable(route_matcher):
            raise ValueError("route_matcher必须是可调用对象")

        with self._lock:
            self.route_matcher = route_matcher
            logger.info("已自定义route_matcher策略")

        return self

    def set_cors_handle(
        self, cors_handle: Callable[[Any, Any, Any], None]
    ) -> "AuthStrategy":
        """
        CORS 策略处理函数

        :param cors_handle: CORS处理函数
        :return: 当前实例，支持链式调用
        :raises ValueError: 当参数不是可调用对象时抛出
        """
        if not callable(cors_handle):
            raise ValueError("cors_handle必须是可调用对象")

        with self._lock:
            self.cors_handle = cors_handle
            logger.info("已自定义cors_handle策略")

        return self
